﻿using System;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
using TaskManager.Domain.Repositories;

namespace TaskManager.DataAccess.Repositories
{
    /// <summary>
    /// Generic repository
    /// </summary>
    /// <typeparam name="TEntity">Entity type</typeparam>
    /// <typeparam name="TKey">Entity key type</typeparam>
    public class Repository<TEntity, TKey> : IRepository<TEntity, TKey>
        where TEntity: class, IEntity<TKey>
    {
        private readonly DbContext _context;
        private readonly DbSet<TEntity> _dbSet;

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="context">Database context</param>
        /// <param name="dbSet">Entity set</param>
        public Repository(DbContext context, DbSet<TEntity> dbSet)
        {
            _context = context;
            _dbSet = dbSet;
        }

        /// <summary>
        /// Returns all available entities
        /// </summary>
        public IQueryable<TEntity> All
        {
            get { return _dbSet; }
        }

        /// <summary>
        /// Returns all available entities retrieving related data (eager loading)
        /// </summary>
        /// <param name="includeProperties">Expression that specifies list of properties to load</param>
        /// <returns>Collection of entities</returns>
        public IQueryable<TEntity> AllIncluding(params Expression<Func<TEntity, object>>[] includeProperties)
        {
            IQueryable<TEntity> query = _dbSet;
            foreach (var includeProperty in includeProperties)
            {
                query = query.Include(includeProperty);
            }
            return query;
        }

        /// <summary>
        /// Searches for the specified entity
        /// </summary>
        /// <param name="id">Entity identifier</param>
        /// <returns>Entity object or null if entity was not found</returns>
        public TEntity Find(TKey id)
        {
            return _dbSet.Find(id);
        }

        /// <summary>
        /// Inserts new entity
        /// </summary>
        /// <param name="entity">Entity</param>
        public void Insert(TEntity entity)
        {
            _dbSet.Add(entity);
        }

        /// <summary>
        /// Updates entity in the database
        /// </summary>
        /// <param name="entity">Entity</param>
        public void Update(TEntity entity)
        {
            _context.Entry(entity).State = EntityState.Modified;
        }

        /// <summary>
        /// Deletes entity
        /// </summary>
        /// <param name="id">Entity identifier</param>
        public void Delete(TKey id)
        {
            var entity = _dbSet.Find(id);
            _dbSet.Remove(entity);
        }
    }
}